﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Linq;
using System.ServiceModel;
using System.Collections.Concurrent;
using System.Threading;
using FinPlusCommon;
using u = FinPlusUtility.Utility;
using log = Logger.Logger;

namespace FinPlusWCFService
{
    internal class FinPlusService
    {
        public IFinPlusComp Component { get; private set; }
        public bool HasCompleted { get; private set; }
        public bool HasStarted { get; private set; }

        private readonly ConcurrentDictionary<string, IFunctionCallback> _callbacks;
        private readonly string _serviceName, _serviceType, _parms, _configFolder;
        private readonly string[] _targetAssemblys;
        private readonly int _asyncWait, _asyncRetry;
        private readonly u.Env _env;

        //contruct
        public FinPlusService(u.Env env, string serviceName, string serviceType, string parms, string guid, string targetAssemblys, string configFolder , int asyncWait, int asyncRetry)
        {
            _env = env;
            _serviceName = serviceName;
            _serviceType = serviceType;
            _parms = parms;
            _targetAssemblys = u.Split(targetAssemblys);
            _configFolder = configFolder;
            _asyncWait = asyncWait;
            _asyncRetry = asyncRetry;
            _callbacks = new ConcurrentDictionary<string, IFunctionCallback>();
        }

        //public
        public void Start()
        {
            HasStarted = true;

            var path = string.Format("{0}{1}\\{2}.Service", AppDomain.CurrentDomain.BaseDirectory, _configFolder, _serviceType);
            var funcs = Functions.ParseFunctions(path, "root/current", _parms);
            log.Log(Logger.Level.Info, string.Format("Service {0} for funcs {1}", _serviceType, funcs.ToString()));

            Component = (IFinPlusComp)FinPlusDInject.Get.Construct(funcs, _serviceType, _env).Object;
            Component.Adaptor.FuncUpdate += Func_Changed;
        }

        public void Dispose(string guid)
        {
            IFunctionCallback callback = null;
            if (_callbacks.ContainsKey(guid))
                _callbacks.TryRemove(guid, out callback);
        }

        public void SendResults(string guid)
        {
            try
            {
                var callback = OperationContext.Current.GetCallbackChannel<IFunctionCallback>();
                _callbacks[guid] = callback;

                 foreach (var func in Component.Adaptor.Values)
                        callback.DispatchFunction(_env, _serviceName, guid, func.Cast<Item>(), ChangeType.New);

                callback.DispatchCompleted(_env, _serviceName, guid);

                HasCompleted = true;
            }
            catch (ServiceConnException e)
            {
                log.Log(Logger.Level.Error, e.Message, e);
            }
        }

        public void WaitCompleted()
        {
            for (int x = 0; x < _asyncRetry; x++)
            {
                Thread.Sleep(_asyncWait);
                if (HasCompleted)
                    break;
            }
        }

        //events
        private void Func_Changed(object s, ItemEventArgs a)
        {
            try
            {
                var func = (Function)s;
                foreach (var callback in _callbacks)
                {
                    if (a.ChangeType == ChangeType.Altered)
                        callback.Value.DispatchItem(_env, _serviceName, callback.Key, func.Id, (Item)a.Item, a.ChangeType);
                    else
                        callback.Value.DispatchFunction(_env, _serviceName, callback.Key, func.Cast<Item>(), a.ChangeType);
                }
            }
            catch (ServiceConnException e)
            {
                log.Log(Logger.Level.Error, e.Message, e);
            }
        }
    }
}
